/* 
 * File:   gLinkedList.h
 * Author: jorge
 *
 * Created on May 2, 2013, 8:32 PM
 */

#ifndef GLINKEDLIST_H
#define	GLINKEDLIST_H
#include "exception.h"

template<class T>
struct LinkedNode
{
public:
    LinkedNode(T data) : data_(data)
    {
        this->next_ = 0;
        this->previous_ = 0;
    }

    ~LinkedNode()
    {
        if(next_)
        {
            delete this->next_;
            this->next_ = 0;
        }
    }

    bool operator>=(const LinkedNode<T> node)
    {
        return this->data_ >= node.data_;
    }

    T& find(T t)
    {
        if(t == this->data_)
        {
            return this->data_;
        }else 
        {
            if(!this->next_)
            {
                throw NonInitializedReference();
            }else
            {
                return this->next_->find(t);
            }
        }
    }

public:
    T data_;
    struct LinkedNode* next_;
    struct LinkedNode* previous_;
};

template <class T>
class GLinkedList
{
public:

    GLinkedList()
    {
        this->size_ = 0;
        this->head_ = 0;
    }

    ~GLinkedList()
    {
        if(this->head_)
        {
            delete this->head_;
            this->head_ = 0;
            this->size_ = 0;
        }
    }

    int size() const
    {
        return this->size_;
    }

    void add(const T& data)
    {
        LinkedNode<T>* newNode = new LinkedNode<T > (data);
        LinkedNode<T>* temp = this->head_;
        if(this->head_ == 0)
        {
            this->head_ = newNode;
        }
        else
        {
            while(temp->next_)
                temp = temp->next_;
            temp->next_ = newNode;
        }
        this->size_++;
    }

    void addSort(const T& data)
    {
        LinkedNode<T>* newNode = new LinkedNode<T > (data);
        if(!this->head_)
        {
            this->head_ = newNode;
        }else
        {
            addSort(this->head_, newNode);
        }
        this->size_++;
    }

    bool isEmpty()
    {
        return this->size_ == 0;
    }

    T get(int pos)
    {
        if(this->head_ && pos >= 0 && pos < this->size_)
        {
            LinkedNode<T>* temp = this->head_;
            while(pos-- && temp->next_)
                temp = temp->next_;
            return temp->data_;
        }else
            throw NonInitializedReference();
    }

    void remove(int pos)
    {
        T aux;
        remove(pos, aux);
    }

    void remove(int pos, T& removed)
    {
        LinkedNode<T>* previous = this->head_;
        LinkedNode<T>* current = previous->next_;
        LinkedNode<T>* nodeToDelete = 0;

        if(!(pos >= 0 || pos < this->size_))
            throw NonInitializedReference();

        if(pos == 0)
        {
            this->head_ = previous->next_;
            nodeToDelete = previous;
            if(this->head_)
            {
                this->head_->previous_ = 0;
            }
        }else
        {
            pos--;
            while(pos-- && current)
            {
                previous = current;
                current = current->next_;
            }
            previous->next_ = current->next_;
            nodeToDelete = current;
        }
        removed = nodeToDelete->data_;
        nodeToDelete->next_ = 0;
        delete nodeToDelete;
        nodeToDelete = 0;
        previous = 0;
        this->size_--;
    }

    T& find(T t)
    {
        if(!this->head_)
        {
            throw NonInitializedReference();
        }else
        {
            return this->head_->find(t);
        }
    }
private:

    void addSort(LinkedNode<T>* & node, LinkedNode<T>* & newNode)
    {
        if(*node >= *newNode)
        {
            insert(node, newNode);
        }else
        {
            if(!node->next_)
            {
                node->next_ = newNode;
                newNode->previous_ = node;
            }else
            {
                addSort(node->next_, newNode);
            }
        }
    }

    void insert(LinkedNode<T>* & node, LinkedNode<T>* & newNode)
    {
        newNode->previous_ = node->previous_;
        newNode->next_ = node;

        if(!newNode->previous_)
        {
            this->head_ = newNode;
            newNode->next_->previous_ = newNode;
        }else
        {
            newNode->next_->previous_ = newNode;
            newNode->previous_->next_ = newNode;
        }
    }
public:
    int size_;
    LinkedNode<T>* head_;
};

#endif	/* GLINKEDLIST_H */

